import {
  IStruct,
  ELEMENT_NOT_FOUND,
  formatToString,
  AbstractStruct,
  commonToString,
  StructConstructor,
  thorwError,
  ErrorEnum
} from "../../shared";
import { ListNode } from "./list-node";
import { hashCode } from "../../shared/utils/hash";
/**
 * 链表构造器
 */
export interface IListConstructor<T> extends StructConstructor {
  new (datas: T[]): IList<T>;
  readonly [Symbol.species]: IListConstructor<T>;
  readonly prototype: IList<T>;
  of<T>(...datas: T[]): IList<T>;
  from<T>(datas: T[]): IList<T>;
}
/**
 * 列表接口
 */
export interface IList<T> extends IStruct {
  /**
   * 获取头节点，
   * 返回节点的值
   */
  first: T | null;
  /**
   * 获取尾元素，
   * 返回节点值
   */
  last: T | null;
  /**
   * 是否包含某个元素
   * @param {T} element 元素类型
   */
  has(element: T): boolean;
  /**
   * 在index位置插入元素
   * @param {T} element 插入元素值
   * @param {number} index 插入的位置下标，若不传则默认为最后一位元素
   */
  add(element: T, index?: number): void;
  /**
   * 获index位置的元素
   * @param {number} index 元素的下标
   */
  get(index: number): T;
  /**
   * 设置index位置的元素，返回原位置元素
   * @param {number} index 元素的下标
   * @param {T} element 更改的值
   */
  set(index: number, element: T): T;
  /**
   * 删除元素，
   * 当传入元素下标返回删除的节点值，
   * 删除成功返回值
   * @param {number} element 传入元素的下标
   */
  delete(index: number): T;
  /**
   * 获取元素的index，
   * 元素的下标，
   * 若不存在返回-1
   * @param {T} element 元素的值
   */
  indexOf(element: T): number;
  /**
   * 在头部添加一个节点
   * @param {T} element 节点的值
   */
  addFirst(element: T): void;
  /**
   * 删除头节点，
   * 返回删除的节点值
   */
  deleteFirst(): T;
  /**
   * 在尾部添加一个元素
   * @param {T} element 值
   */
  addLast(element: T): void;
  /**
   * 删除尾元素，
   * 返回删除的元素
   */
  deleteLast(): T;
  [Symbol.iterator](): IterableIterator<T>;
  entries(): IterableIterator<[number, T]>;
  keys(): IterableIterator<number>;
  values(): IterableIterator<T>;
}
/**
 * 列表抽象类
 */
export abstract class AbstractList<T> extends AbstractStruct
  implements IList<T> {
  abstract add(element: T, index?: number): void;
  abstract delete(index: number): T;
  get first(): T | null {
    return this.empty ? null : this.get(0);
  }
  get last(): T | null {
    return this.empty ? null : this.get(this._size - 1);
  }
  get hash(): number {
    let res = 0;
    for (const el of this) res += hashCode(el);
    return res;
  }
  protected _firstNode: ListNode<T> | null = null;
  has(element: T): boolean {
    return this.indexOf(element) !== ELEMENT_NOT_FOUND;
  }
  addFirst(element: T): void {
    this.add(element, 0);
  }
  deleteFirst(): T {
    return this.delete(0);
  }
  addLast(element: T): void {
    this.add(element);
  }
  deleteLast(): T {
    return this.delete(this._size - 1);
  }
  indexOf(element: T): number {
    for (const [k, v] of this.entries())
      if (hashCode(v) === hashCode(element)) return k;
    return ELEMENT_NOT_FOUND;
  }
  get(index: number): T {
    this.checkRange(index);
    let el: T = {} as T;
    for (const [k, v] of this.entries())
      if (k === index) {
        el = v;
        break;
      }
    return el;
  }
  set(index: number, element: T): T {
    this.checkRange(index);
    let el = {} as T;
    for (const [k, v] of this.entriesForNode())
      if (k === index) {
        el = v.element;
        v.element = element;
        break;
      }
    return el;
  }
  clear(): void {
    this._firstNode = null;
    super.clear();
  }
  *[Symbol.iterator](): IterableIterator<T> {
    for (const v of this.values()) yield v;
  }
  *keys(): IterableIterator<number> {
    for (const [k] of this.entries()) yield k;
  }
  *values(): IterableIterator<T> {
    for (const [, v] of this.entries()) yield v;
  }
  *entries(): IterableIterator<[number, T]> {
    for (const [k, v] of this.entriesForNode()) yield [k, v.element];
  }
  toString(): string {
    let s = formatToString(this[Symbol.toStringTag], ["Size", this._size]);
    if (this._size !== 0) {
      s += ",Elements=[";
      for (const val of this) s += commonToString(val) + ",";
      s = s.substr(0, s.length - 1) + "]";
    }
    return s;
  }
  /**
   * 添加判断是否在元素数量内
   * @param index 下标
   */
  protected checkRange(index: number): void {
    if (index < 0 || index >= this._size) {
      this.thorwError(index);
    }
  }
  /**
   * 添加判断是否在元素数量+1内
   * @param index
   */
  protected checkAddRange(index: number): void {
    if (index < 0 || index > this.size) {
      this.thorwError(index);
    }
  }
  /**
   * 内部调用，
   * 返回IterableIterator,
   * key: number,
   * value:{element:T}
   */
  protected *entriesForNode(): IterableIterator<[number, ListNode<T>]> {
    let node = this._firstNode;
    let c = 0;
    while (c !== this._size && node !== null) {
      yield [c, node];
      node = node.next;
      c++;
    }
  }
  /**
   * 抛出越界异常
   * @param index 下标
   */
  protected thorwError(index: number): never {
    thorwError(
      formatToString(
        this[Symbol.toStringTag],
        ["Index", index],
        ["Size", this._size]
      ),
      ErrorEnum.range
    );
  }
}
